package com.gitee.l0km.beanfilter.interceptor;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.beanfilter.core.CodecPhase;
import com.gitee.l0km.beanfilter.core.CodecScope;
import com.gitee.l0km.beanfilter.core.ICombinedFilter;
import com.gitee.l0km.beanfilter.core.IFieldFilter;
import com.gitee.l0km.beanfilter.core.IFilter;
import com.gitee.l0km.beanfilter.core.IFilterContainer;
import com.gitee.l0km.beanfilter.core.IValueFilter;
import com.gitee.l0km.beanfilter.interceptor.BaseContainer.FieldFilterContainer;
import com.gitee.l0km.beanfilter.interceptor.BaseContainer.ValueFilterContainer;
import com.gitee.l0km.casban.CommonPredicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;

public class FilterContainer  implements IFilterContainer{
	final BaseContainer<IFieldFilter> fieldFilterContainer = new BaseContainer<>(IFieldFilter.class);
	final BaseContainer<IValueFilter> valueFilterContainer = new BaseContainer<IValueFilter>(IValueFilter.class) {
		@Override
		protected void add(FilterMetadata<IValueFilter> meta) {
			FilterMetadata<IValueFilter> old = metadatas.stream()
					.filter(m->m.beanClass.equals(meta.beanClass)
								&& m.phase.equals(meta.phase)
								&& m.scope.equals(meta.scope))
					.findFirst()
					.orElse(null);
			if(null != old) {
				if(old.filter instanceof ValueFilterContainer) {
					((ValueFilterContainer)old.filter).combine(meta);
				}else {
					ValueFilterContainer combined = new ValueFilterContainer().combine(old,meta);
					metadatas.remove(old);
					 super.add(FilterMetadata.of(meta.phase, meta.scope,meta.beanClass, combined));
				}
			}else {
				super.add(meta);
			}
		}
	};
	/**
	 * 序列化相关的所有Java Bean类型<br> 
	 * 从方法返回值类型中获取 
	 */
	private final ImmutableSet<Class<?>> outputAssociated;
	/** 
	 * 反序列化相关的所有Java Bean类型<br> 
	 * 从方法参数类型中获取
	 */
	private final ImmutableSet<Class<?>> inputAssociated;
	public FilterContainer() {
		this(null, null);
	}

	public FilterContainer(Set<Class<?>> outputAssociated, Set<Class<?>> inputAssociated) {
		this.outputAssociated = ImmutableSet.copyOf(firstNonNull(outputAssociated, Collections.emptySet()) );
		this.inputAssociated = ImmutableSet.copyOf(firstNonNull(inputAssociated, Collections.emptySet()));
	}
	
	/**
	 * 返回从方法返回值类型中获取的序列化相关的所有Java Bean类型<br>
	 * 如果{@code scope}为非 {@link CodecScope#THRIFT} ,
	 * 则返回的关联类型集合中Thrift装饰类型({@link com.gitee.l0km.xthrift.base.ThriftDecorator})会被转换为其代理的类型，
	 * 确保在THRIFT环境下服务方法中使用JACKSON或FASTJSON进行序列化时，对应的过滤器有效。<br>
	 * 因为Thrift装饰类型对于应用层是透明的，注解中定义的类型都不可能是装饰类型，
	 * 所以THRIFT环境下，使用JACKSON或FASTJSON进行序列化时，如果仍然返回Thrift装饰类型，
	 * 则在执行 {@link #computeBeanTypes(Set, Set)}计算时就会将Thrift装饰类型遗漏掉。导致过滤器失效。
	 * @param scope
	 * @see ThriftDecoratorSupport#delegateClasses(Iterable)
	 * @see CombinedFilter#associateClasses(CodecPhase, CodecScope)
	 */
	private ImmutableSet<Class<?>> getOutputAssociated(CodecScope scope) {
		if (CodecScope.THRIFT.equals(scope)) {
			return outputAssociated;
		}
		return ThriftDecoratorSupport.delegateClasses(outputAssociated);
	}
	/**
	 * 返回从方法参数类型中获取的反序列化相关的所有Java Bean类型<br>
	 * @param scope
	 * @see #getOutputAssociated(CodecScope)
	 */
	private ImmutableSet<Class<?>> getInputAssociated(CodecScope scope) {
		if (CodecScope.THRIFT.equals(scope)) {
			return inputAssociated;
		}
		return ThriftDecoratorSupport.delegateClasses(inputAssociated);
	}

	@Override
	public boolean isEmpty() {
		return fieldFilterContainer.isEmpty() && valueFilterContainer.isEmpty();
	}
	/**
	 * 返回指定编/解码阶段和作用范围过滤器关联的所有类型
	 * @param phase
	 * @param scope
	 */
	@Override
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public Set<Class<?>> associateClasses(CodecPhase phase, CodecScope scope){
		if(null == phase) {
			return ImmutableSet.of();
		}
		FluentIterable<Class<?>> iterable = FluentIterable.of();
		if(phase.serialization) {
			iterable = iterable.append(getFilter(phase, scope).associateClasses(phase, scope));
		}
		if(phase.deserialization) {
			iterable = iterable.append(getFilter(phase, scope).associateClasses(phase, scope));
		}
		return iterable.toSet();
	}
	/**
	 * 获取 {@code phase}指定编解码阶段及 {@code scope} 指定范围的过滤器<br>
	 * @param phase 必须为 {@link CodecPhase#SERIALIZATION}或{@link CodecPhase#DESERIALIZATION}
	 * @param scope 必须为 {@link CodecScope#FASTJSON},{@link CodecScope#JACKSON}或{@link CodecScope#THRIFT}
	 */
	@Override
	public ICombinedFilter getScenceFilter(CodecPhase phase, CodecScope scope) {
		checkNotNull(phase,"phase is null");
		checkNotNull(scope,"scope is null");
		checkArgument(phase.singlePhase(),"INVALID phase,%s OR %s required",
				CodecPhase.SERIALIZATION,CodecPhase.DESERIALIZATION);
		checkArgument(scope.singleScope(),"INVALID scope, required:%s,%s,%s",
				CodecScope.JACKSON,CodecScope.FASTJSON,CodecScope.THRIFT);
		return getFilter(phase, scope);
	}
	/**
	 * 获取 {@code phase}指定编解码阶段及 {@code scope} 指定范围的过滤器<br>
	 * phase 必须为 {@link CodecPhase#SERIALIZATION}或{@link CodecPhase#DESERIALIZATION}
	 * @param phase 
	 * @param scope
	 */
	@Override
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public ICombinedFilter getFilter(CodecPhase phase,CodecScope scope) {
		checkNotNull(scope,"scope is null");
		checkNotNull(phase,"phase is null");
		return new CombinedFilter(
				new FieldFilterContainer(fieldFilterContainer.getMetadatas(phase, scope)),
				new ValueFilterContainer(valueFilterContainer.getMetadatas(phase, scope)));

	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("FilterContainer [fieldFilterContainer=").append(fieldFilterContainer)
				.append(", valueFilterContainer=").append(valueFilterContainer).append(", serizationAssociated=")
				.append(outputAssociated).append(", deserizationAssociated=").append(inputAssociated)
				.append("]");
		return builder.toString();
	}

	/**
	 * 根据{@code others}提供的类型，删除{@code beanTypes}中所有非{@code others}子类的类
	 * @param beanTypes
	 * @param others
	 */
	private static Set< Class<?>> computeBeanTypes(Set<Class<?>>beanTypes,Set<Class<?>>others){
		if(null != beanTypes &&  null != others) {
			return FluentIterable.from(beanTypes).filter(CommonPredicates.subTypeClassFilter(others)).toSet();
		}
		return firstNonNull(beanTypes,Collections.emptySet());
	}
	public class CombinedFilter implements IValueFilter,IFieldFilter, ICombinedFilter{
		private final FieldFilterContainer fieldFilter;
		private final ValueFilterContainer valueFilter;
		private CombinedFilter(FieldFilterContainer fieldFilter, ValueFilterContainer valueFilter) {
			this.fieldFilter = fieldFilter;
			this.valueFilter = valueFilter;
		}
		/**
		 * 返回过滤器注解(beanClass)中定义的过滤器关联类型
		 */
		private Set<Class<?>>associateClasses(){
			return 	FluentIterable.from(fieldFilter.associateClasses())
						.append(valueFilter.associateClasses())
						.toSet();
		}
		/**
		 * 需要将方法定义的关联类型与注解定义的关联类型进行子类过滤运算,
		 * 得到最终的关联类型集合(不包含抽象的父类)
		 * @see FilterContainer#computeBeanTypes(Set, Set)
		 */
		@Override
		public Set<Class<?>> associateClasses(CodecPhase phase, CodecScope scope){
			Set<Class<?>> filterAssociated = associateClasses();
			Set<Class<?>> se = phase.serialization 
					? computeBeanTypes(getOutputAssociated(scope), filterAssociated)
					: Collections.emptySet();
			Set<Class<?>> de = phase.deserialization 
					? computeBeanTypes(getInputAssociated(scope), filterAssociated)
					: Collections.emptySet();
			return FluentIterable.from(se).append(de).toSet();
		}
		@Override
		public boolean isEmpty() {
			return isEmpty(null);
		}

		@Override
		public boolean isEmpty(Class<? extends IFilter> type) {
			if(IValueFilter.class.equals(type)) {
				return valueFilter.isEmpty();
			}else if(IFieldFilter.class.equals(type)) {
				return fieldFilter.isEmpty();
			}
			return fieldFilter.isEmpty() && valueFilter.isEmpty();
		}
		@Override
		public IFieldFilter getFieldFilterOrDefault() {
			return !fieldFilter.isEmpty() ? fieldFilter : IFieldFilter.DEFAULT_INSTANCE;
		}
		@Override
		public IValueFilter getValueFilterOrDefault() {
			return !valueFilter.isEmpty() ? valueFilter : IValueFilter.DEFAULT_INSTANCE;
		}
		@Override
		public boolean permit(Class<?> clazz, String fieldName) {
			return fieldFilter.permit(clazz, fieldName);
		}
		
		@Override
		public Object process(Class<?> clazz, String name, Object value) {
			return valueFilter.process(clazz, name, value);
		}

		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("CombinedFilter [fieldFilter=").append(fieldFilter).append(", valueFilter=")
					.append(valueFilter).append("]");
			return builder.toString();
		}	
	}

}
